﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.Diagnostics;
using System.IO;

namespace WindowsFormsApplication5 {
    public partial class Form1 : Form {
        private Bitmap image;
        private Point[,] Points;
        private int forGroundCounter = 1;
        private int roundCounter = 0;
        private ArrayList EightConArray = new ArrayList();
        private ArrayList Blobs;
        private int currentLabel = 1;
        private int [] blobArray;
        private ArrayList pixelsToCheck1 = new ArrayList();
        private ArrayList pixelsToCheck2 = new ArrayList();

        public Form1() {
            InitializeComponent();
        }

        private void openToolStripMenuItem_Click(object sender, EventArgs e) { // open image button
            if (openFileDialog1.ShowDialog() != DialogResult.Cancel) {
                // Display the image in the PictureBox.
                openFileDialog1.Filter = "Jpg (*.jpg)|*.jpg|Gif (*.gif)|*.gif|Bmp(*.bmp)|*.bmp";
                pictureBox1.Image = Image.FromFile(openFileDialog1.FileName);

                image = new Bitmap(pictureBox1.Image);
            }
        }

        private void closeToolStripMenuItem_Click(object sender, EventArgs e) { // close image button
            pictureBox1.Image = null;
            pictureBox1.Refresh();
        }

        private void algorithm1ToolStripMenuItem_Click(object sender, EventArgs e) { // algorithm 1, only down passes ( left - top to bottom right. )
            
            Stopwatch timer = new Stopwatch();

            timer.Start();

            Points = new Point[image.Width, image.Height];

            assignPoints();

            roundCounter = 0;

            Blobs = new ArrayList();

            while (Blobs.Count < 10)
            {
                runDownPass();
                assignBlobs();
                roundCounter++;
            }

            timer.Stop();

            writeToText("kek");

            writeToText("DownPass");
            label1.Text = "Aantal passes: " + roundCounter + "";
            textBox1.AppendText("Verstreken in: " + timer.ElapsedMilliseconds + " Miliseconds " + "Met " + Blobs.Count + " blobs aanwezig" + " en " + roundCounter + " passes" + "\n");
            Points = null;
            Blobs = null;
        }

        private void algorithm2ToolStripMenuItem_Click(object sender, EventArgs e) { // algorithm 2, switching up and down passes
            Stopwatch timer = new Stopwatch();
            timer.Start();


            Points = new Point[image.Width, image.Height];
            assignPoints();
            roundCounter = 0;
            Blobs = new ArrayList();
            while (Blobs.Count != 20) {

                runDownPass();
                runUpPass();

                assignBlobs();
                
                roundCounter++;
            }
            timer.Stop();
            writeToText("DownUp-Pass");
            label1.Text = "Aantal passes: " + roundCounter + "";
            textBox1.AppendText("Verstreken in: " + timer.ElapsedMilliseconds + " Miliseconds " + "Met " + Blobs.Count + " blobs aanwezig" + " en " + roundCounter + " passes" + "\n");
            Points = null;
            Blobs = null;
        }

        private void algorithm3ToolStripMenuItem_Click(object sender, EventArgs e) { // growing selection algorithm
            Stopwatch timer = new Stopwatch();
            timer.Start();

            Points = new Point[image.Width, image.Height];
            assignPointsGrowingSelection();

            roundCounter = 0;

            Blobs = new ArrayList();

                runGrowingSelection();

                assignBlobs();

            timer.Stop();
            writeToText("GrowingSelection");
            label1.Text = "Aantal passes: " + roundCounter + "";
            textBox1.AppendText("Verstreken in: " + timer.ElapsedMilliseconds + " Miliseconds " + "Met " + Blobs.Count + " blobs aanwezig" + " en " + (roundCounter+1) + " passes" + "\n");
            Points = null;
            Blobs = null;
        }

        private void colorBlobsToolStripMenuItem_Click(object sender, EventArgs e) {
            Stopwatch timer = new Stopwatch();
            timer.Start();


            Points = new Point[image.Width, image.Height];

            assignPoints();

            roundCounter = 0;

            Blobs = new ArrayList();

                runDownPass();
                assignBlobs();


            Bitmap newImage = new Bitmap(pictureBox1.Image);
            Color newColor = Color.FromArgb(255, 0, 255); // 360 degrees in hue.
            foreach (Blob b in Blobs) {
                int lineCounter = 0;
                foreach (Point p in Points) {
                    if (p.id == b.id && lineCounter <= b.pixelInBlob) {
                        newImage.SetPixel(p.xPos, p.yPos, newColor);//sets the pixel
                        lineCounter++;
                    }
                }
                double hue = newColor.GetHue();
                double saturation = newColor.GetSaturation();
                double brightness = newColor.GetBrightness();
                hue = (hue / 360.0F);
                hue -= ((360 / Blobs.Count-1) / 360.0F);
                if (hue > 0.00) {
                    newColor = calculateHueToRGB(hue, saturation, brightness);
                }
                else {
                    newColor = calculateHueToRGB(0, saturation, brightness);
                }
            }

            pictureBox1.Image = newImage;
            pictureBox1.Refresh();
            newImage.Save("DividedHue.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
            
            timer.Stop();
            writeToText("DownPass");
            label1.Text = "Aantal passes: " + roundCounter + "";
            textBox1.AppendText("Verstreken in: " + timer.ElapsedMilliseconds + " Miliseconds " + "Met " + Blobs.Count + " blobs aanwezig" + " en " + roundCounter + " passes" + "\n");

            Points = null;
            Blobs = null;
        }

        private Color calculateHueToRGB(double h, double sl, double l) {
            double v;
            double r, g, b;
            r = l;   // default to gray
            g = l;
            b = l;
            v = (l <= 0.5) ? (l * (1.0 + sl)) : (l + sl - l * sl);
            if (v > 0) {
                double m;
                double sv;
                int sextant;
                double fract, vsf, mid1, mid2;

                m = l + l - v;
                sv = (v - m) / v;
                h *= 6.0;
                sextant = (int)h;
                fract = h - sextant;
                vsf = v * sv * fract;

                mid1 = m + vsf;
                mid2 = v - vsf;

                 switch(sextant) {
                        case 0:
                              r = v;
                              g = mid1;
                              b = m;
                              break;

                        case 1:
                              r = mid2;
                              g = v;
                              b = m;
                              break;

                        case 2:
                              r = m;
                              g = v;
                              b = mid1;
                              break;

                        case 3:
                              r = m;
                              g = mid2;
                              b = v;
                              break;

                        case 4:
                              r = mid1;
                              g = m;
                              b = v;
                              break;

                        case 5:
                              r = v;
                              g = m;
                              b = mid2;
                              break;
                  }
            }
            Color rgb = Color.FromArgb(Convert.ToByte(r * 255.0f), Convert.ToByte(g * 255.0f), Convert.ToByte(b * 255.0f));
            return rgb;
      }

        private void runDownPass()
        {
            for (int y = 0; y < image.Height; y++)
            {  // Iterate over all the pixels in the image.
                for (int x = 0; x < image.Width; x++)
                {

                    if (Points[x, y].id > 0)
                    {
                        Points[x, y].id = achtConnected(x, y);
                    }
                }
            }
        }

        private void runUpPass()
        {
            for (int y = image.Height - 1; y > 1; y--)
            {  // Iterate over all the pixels in the image.
                for (int x = image.Width - 1; x > 1; x--)
                {

                    if (Points[x, y] != null && Points[x, y].id > 0)
                    {

                        Points[x, y].id = achtConnected(x, y);
                    }
                }
            }
        }

        /// <summary>
        /// This function iterates through all the points in the array, and calls the recursive function localize neighbour if the pixel is a for
        /// </summary>
        private void runGrowingSelection()
        { // run the growing selection filter
            for (int y = 1; y < image.Height - 1; y++)
            {  // Iterate over all the pixels in the image.
                for (int x = 1; x < image.Width - 1; x++) // might wanna change object[,] to Point[,]
                {
                    Point Point = (Point)Points[x, y]; // get the corresponding Point.
                    if (Point.id == 1)
                    { //Its a forground pixel, so we want to assign this whole blob.
                        Point.id = currentLabel;
                        if (localizeNeighbour(x, y, 1) == true)
                        {
                            processList();
                        }
                        currentLabel++; // increment the current label ( to be given to new blobs )
                    }

                }
            }
        }
        /// <summary>
        /// This function will call upon itself recurively to detect blobs. 
        /// It will check out all the pixels around him, and call itself again if a foreground pixel is found.
        /// </summary>
        /// <param name="xPos">X position of the blob that needs to be checked</param>
        /// <param name="yPos">y position of the blob that needs to be checked</param>
        private Boolean localizeNeighbour(int xPos, int yPos, int insertTo)
        {// recursive function to detect a whole blob.
            Boolean changed = false;

            for (int y = -1; y < 2; y++)
            { // we only want to check 8- connected around us
                for (int x = -1; x < 2; x++)
                { // we only want to check 8- connected around us
                    if ((xPos + x < 0) || (xPos + x > (image.Width - 1)) || (yPos + y < 0) || (yPos + y > (image.Height - 1)))
                    {
                        return false;
                    }
                    Point p = Points[xPos + x, yPos + y]; // make a new Point out of the corresponding pixel ( the one that is beeing checked. )
                    if (p != null)
                    { // if the Point isnt null.
                        if (p.id == 1)
                        { //check if it is a forgroundpixel, and not another blob ( or background ).
                            p.id = currentLabel; //give this Point the current set label.

                            if (insertTo == 1)
                            {
                                pixelsToCheck1.Add(p);
                            }
                            else
                            {
                                pixelsToCheck2.Add(p);
                            }
                            changed = true;
                        }
                    }
                }
            }
            return changed;
        }

        private Boolean processList()
        {
            Boolean haselements;

            for (; ;)
            {
                haselements = false;

                foreach (Point dp in pixelsToCheck1)
                {
                    haselements = true;
                    localizeNeighbour(dp.xPos, dp.yPos, 2);
                }

                pixelsToCheck1.Clear();

                foreach (Point dp in pixelsToCheck2)
                {
                    haselements = true;
                    localizeNeighbour(dp.xPos, dp.yPos, 1);
                }

                pixelsToCheck2.Clear();

                if (haselements == false)
                {
                    break;
                }
            }
            return false;
        }

        private void assignPoints()
        {

            for (int y = 0; y < image.Height; y++)
            {  // Iterate over all the pixels in the image.
                for (int x = 0; x < image.Width; x++)
                {

                    Color waardesPixel = image.GetPixel(x, y); //Select the current pixel, and make a point.

                    Point p = new Point(x, y, 0);

                    if (waardesPixel.R + waardesPixel.G + waardesPixel.B > 500)
                    {
                        p.id = forGroundCounter;
                        
                        forGroundCounter++;
                    }

                    Points[x, y] = p;
                }
            }
        }

        private void assignPointsGrowingSelection(){

            for (int y = 0; y < image.Height; y++) {  // Iterate over all the pixels in the image.

                for (int x = 0; x < image.Width; x++) {

                    Color waardesPixel = image.GetPixel(x, y); //Select the current pixel, and make a point.

                    Point p = new Point(x, y, 0);

                    if (waardesPixel.R + waardesPixel.G + waardesPixel.B > 500) {
                        p.id = 1;
                        Points[x, y] = p;
                        forGroundCounter++;
                    }

                    Points[x, y] = p;
                }
            }
        }

        private int achtConnected(int x, int y)
        {
            EightConArray = new ArrayList();

            int Value = Int32.MaxValue;

            if ((x + 1 < image.Width && x - 1 > 0 && y + 1 < image.Height && y - 1 > 0))
            {

                    EightConArray.Add(Points[x - 1, y - 1]);
                    EightConArray.Add(Points[x - 1, y]);
                    EightConArray.Add(Points[x - 1, y + 1]);
                    EightConArray.Add(Points[x, y - 1]);
                    EightConArray.Add(Points[x, y + 1]);
                    EightConArray.Add(Points[x + 1, y - 1]);
                    EightConArray.Add(Points[x + 1, y]);
                    EightConArray.Add(Points[x + 1, y + 1]);

                foreach(Point point in EightConArray){
                    if (point.id > 0)
                    {
                        if (point.id < Value)
                        {
                            Value = point.id;
                        }
                    }
                }

                if (Value == Int32.MaxValue)
                {
                    Value = 0;
                }

                EightConArray.Clear();


                return Value;

            }
            else
            {
                return 0;
            }
        }

        private void assignBlobs() {

                blobArray = new int[forGroundCounter];
                Blobs = new ArrayList();

                foreach (Point p in Points) {
                    if (p.id > 0) {
                        blobArray[p.id]++;
                    }
                }

                for (int i = 0; i < blobArray.Length; i++) {
                    if (blobArray[i] > 0) {
                        Blob b = new Blob(i, blobArray[i]);
                        Blobs.Add(b);
                    }
                }
        }

        private void writeToText(String Name) {
            // create a writer and open the file
            TextWriter tw = new StreamWriter(Name + ".txt");
            int blobcntr = 0;
                // write a line of text to the file
             foreach (Blob b in Blobs) {
                 tw.WriteLine("----- Blob " + (blobcntr + 1) + ": " + ((Blob)Blobs[blobcntr]).pixelInBlob + " Pixels -----" + "\n\n");
                int lineCounter = 0;
                    foreach (Point p in Points) {
                        if (p.id == b.id && lineCounter < b.pixelInBlob) {
                            tw.Write(p.CoordinatesString() + ", ");
                            lineCounter++;
                        }

                        if (p.id == b.id && lineCounter == (b.pixelInBlob)) {
                            tw.Write(p.CoordinatesString());
                            tw.WriteLine("\n");
                            tw.WriteLine("\n");
                        }
                    }
                 blobcntr++;
                }
                // close the stream
                tw.Close();
        }
    
        private void writeBenchMark(String name, int writes, int counts) {
            TextWriter tw = new StreamWriter(name + ".txt");
            tw.WriteLine(name + ": " + writes + " toekenningen, " + counts + " labels gelezen");
            tw.Close();
        }



    }     
    }